import wx
import wx.stc as stc


class TextEditor(wx.Frame):
    def __init__(self, parent, title):
        super(TextEditor, self).__init__(parent, title=title, size=(800, 600))

        # 创建菜单栏
        self.create_menu_bar()

        # 创建文本控件
        self.text_ctrl = stc.StyledTextCtrl(self, style=wx.TE_MULTILINE | wx.TE_WORDWRAP)
        self.text_ctrl.SetLexer(stc.STC_LEX_NULL)  # 设置为纯文本模式

        # 设置默认样式
        self.text_ctrl.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:Courier New,size:12")

        # 设置窗口布局
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.text_ctrl, 1, wx.EXPAND)
        self.SetSizer(sizer)

        # 绑定事件
        self.Bind(wx.EVT_CLOSE, self.on_close)

        # 初始化状态
        self.filename = None
        self.modified = False

    def create_menu_bar(self):
        menu_bar = wx.MenuBar()

        # 文件菜单
        file_menu = wx.Menu()
        file_menu.Append(wx.ID_OPEN, "&Open\tCtrl+O", "Open a file")
        file_menu.Append(wx.ID_SAVE, "&Save\tCtrl+S", "Save the current file")
        file_menu.AppendSeparator()
        file_menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit the application")
        self.Bind(wx.EVT_MENU, self.on_open, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.on_save, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.on_exit, id=wx.ID_EXIT)
        menu_bar.Append(file_menu, "&File")

        # 格式菜单
        format_menu = wx.Menu()
        self.bold_item = format_menu.Append(wx.ID_ANY, "&Bold\tCtrl+B", "Make text bold")
        self.italic_item = format_menu.Append(wx.ID_ANY, "&Italic\tCtrl+I", "Make text italic")
        self.Bind(wx.EVT_MENU, self.on_bold, self.bold_item)
        self.Bind(wx.EVT_MENU, self.on_italic, self.italic_item)
        menu_bar.Append(format_menu, "&Format")

        self.SetMenuBar(menu_bar)

    def on_open(self, event):
        with wx.FileDialog(self, "Open File", wildcard="Text files (*.txt)|*.txt|All files (*)|*",
                           style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return
            pathname = fileDialog.GetPath()
            try:
                with open(pathname, "r") as file:
                    self.text_ctrl.SetValue(file.read())
                    self.filename = pathname
                    self.modified = False
            except IOError:
                wx.LogError(f"Cannot open file '{pathname}'.")

    def on_save(self, event):
        if self.filename is None:
            self.on_save_as(event)
        else:
            try:
                with open(self.filename, "w") as file:
                    file.write(self.text_ctrl.GetValue())
                    self.modified = False
            except IOError:
                wx.LogError(f"Cannot save current data in file '{self.filename}'.")

    def on_save_as(self, event):
        with wx.FileDialog(self, "Save File As", wildcard="Text files (*.txt)|*.txt|All files (*)|*",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:
            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return
            pathname = fileDialog.GetPath()
            try:
                with open(pathname, "w") as file:
                    file.write(self.text_ctrl.GetValue())
                    self.filename = pathname
                    self.modified = False
            except IOError:
                wx.LogError(f"Cannot save current data in file '{pathname}'.")

    def on_exit(self, event):
        if self.modified:
            dialog = wx.MessageDialog(self, "Do you want to save changes before closing?", "Confirm Exit",
                                      wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
            result = dialog.ShowModal()
            if result == wx.ID_YES:
                self.on_save(event)
            elif result == wx.ID_CANCEL:
                return
        self.Destroy()

    def on_bold(self, event):
        style = self.text_ctrl.GetStyleAt(self.text_ctrl.GetCurrentPos())
        if style & stc.STC_STYLE_BOLD:
            self.text_ctrl.StartStyling(self.text_ctrl.GetSelectionStart(), 0xff)
            self.text_ctrl.SetStyling(self.text_ctrl.GetSelectionEnd() - self.text_ctrl.GetSelectionStart(), 0)
        else:
            self.text_ctrl.StartStyling(self.text_ctrl.GetSelectionStart(), 0xff)
            self.text_ctrl.SetStyling(self.text_ctrl.GetSelectionEnd() - self.text_ctrl.GetSelectionStart(),
                                      stc.STC_STYLE_BOLD)

    def on_italic(self, event):
        style = self.text_ctrl.GetStyleAt(self.text_ctrl.GetCurrentPos())
        if style & stc.STC_STYLE_ITALIC:
            self.text_ctrl.StartStyling(self.text_ctrl.GetSelectionStart(), 0xff)
            self.text_ctrl.SetStyling(self.text_ctrl.GetSelectionEnd() - self.text_ctrl.GetSelectionStart(), 0)
        else:
            self.text_ctrl.StartStyling(self.text_ctrl.GetSelectionStart(), 0xff)
            self.text_ctrl.SetStyling(self.text_ctrl.GetSelectionEnd() - self.text_ctrl.GetSelectionStart(),
                                      stc.STC_STYLE_ITALIC)

    def on_close(self, event):
        if self.modified:
            dialog = wx.MessageDialog(self, "Do you want to save changes before closing?", "Confirm Exit",
                                      wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
            result = dialog.ShowModal()
            if result == wx.ID_YES:
                self.on_save(event)
            elif result == wx.ID_CANCEL:
                event.Veto()
                return
        self.Destroy()


if __name__ == "__main__":
    app = wx.App(False)
    frame = TextEditor(None, "Simple Text Editor")
    frame.Show(True)
    app.MainLoop()